package com.example.customview.view

import android.animation.ObjectAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import android.widget.OverScroller
import androidx.core.animation.doOnEnd
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.ViewCompat
import com.example.customview.R
import com.example.customview.util.getBitmap
import com.sample.base.utils.dp
import kotlin.math.max
import kotlin.math.min

/**
 * 双击放大/缩小 带动画效果
 */
class ScalableImageView(context: Context, attrs: AttributeSet) : View(context, attrs),
    GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener, Runnable {

    companion object {
        val IMAGE_SIZE = 300.dp
    }

    private val paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val bitmap = this.getBitmap(IMAGE_SIZE, R.drawable.avatar)
    private var originalOffsetX = 0f
    private var originalOffsetY = 0f
    private var scrollOffsetX = 0f // 手指滑动的偏移
    private var scrollOffsetY = 0f
    private var smallScale = 0f// 小图放缩比
    private var bigScale = 0f// 大图放缩比
    private var bigScaleFactor = 1.5f//为了让放大的图片足够大，可以上下左右滑动
    private var big = false
    private val scroller = OverScroller(context)// 用来计算惯性滑动

    private var scaleFraction = 0f
        set(value) {
            field = value
            invalidate()
        }

    private val scaleAnimator: ObjectAnimator by lazy {
        ObjectAnimator.ofFloat(this, "scaleFraction", 0f, 1f).apply {
            doOnEnd {
                if (!big) {
                    scrollOffsetX = 0f
                    scrollOffsetY = 0f
                }
            }
        }
    }

    private val gestureDetector = GestureDetectorCompat(context, this).apply {
        setOnDoubleTapListener(this@ScalableImageView)
    }

    // 缩放监听
    private val zScaleGestureDetectorListener = ZScaleGestureListener()
    private val scaleGestureDetector = ScaleGestureDetector(context, zScaleGestureDetectorListener)


    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        originalOffsetX = (width - IMAGE_SIZE) / 2f
        originalOffsetY = (height - IMAGE_SIZE) / 2f
        if (bitmap.width / bitmap.height.toFloat() > width / height.toFloat()) {
            // 说明图片是胖一点的图片
            smallScale = width / bitmap.width.toFloat()
            bigScale = height / bitmap.height.toFloat() * bigScaleFactor
        } else {
            smallScale = height / bitmap.height.toFloat()
            bigScale = width / bitmap.width.toFloat() * bigScaleFactor
        }

    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
//        val scale = if (big) bigScale else smallScale
        canvas.translate(scrollOffsetX * scaleFraction, scrollOffsetY * scaleFraction)
        val scale = smallScale + (bigScale - smallScale) * scaleFraction
        canvas.scale(scale, scale, width / 2f, height / 2f)
        canvas.drawBitmap(bitmap, originalOffsetX, originalOffsetY, paint)
    }

    /**
     * 把事件交给 gestureDetector
     */
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        /**
         * 同时支持双击和捏撑
         */
       /* scaleGestureDetector.onTouchEvent(event)
        if (!scaleGestureDetector.isInProgress) {
            gestureDetector.onTouchEvent(event)
        }
        return true*/

        // 捏撑的效果没有实现，所以先只支持双击缩放
        return gestureDetector.onTouchEvent(event)
    }


    /**
     * 用户摸到你，并且摸了 100 ms
     */
    override fun onShowPress(e: MotionEvent) {
    }

    /**
     * 长按监听
     */
    override fun onLongPress(e: MotionEvent) {
    }

    /**
     * 单击的意思，onClick 的替代
     * 当支持长按的时候  -- gestureDetector.setIsLongpressEnabled(true)
     * 必须是快速抬起才算，否侧就当做长按了
     *
     * 返回值的意思，你是否消费了这个点击事件，但是是否消费事件是由  onDown 来决定的，
     * 所以这里的返回值没有意义，返回 true/false 都行
     *
     */
    override fun onSingleTapUp(e: MotionEvent): Boolean {
        return false
    }

    /**
     * 只要你需要拦截事件 这里就要返回 true
     */
    override fun onDown(e: MotionEvent): Boolean {
        return true
    }

    /**
     * 快滑松手之后被调用，惯性滑动
     * @param downEvent 按下的时候的事件
     * @param currentEvent 最新的当前事件
     * @param velocityX 横向的速率；单位时间内的位移
     * @param velocityY 纵向的速率；单位时间内的位移
     *  返回值没有意义
     */
    override fun onFling(
        downEvent: MotionEvent?,
        currentEvent: MotionEvent?,
        velocityX: Float,
        velocityY: Float
    ): Boolean {
        if (big) {
            scroller.fling(
                scrollOffsetX.toInt(), scrollOffsetY.toInt(), velocityX.toInt(), velocityY.toInt(),
                (-(bitmap.width * bigScale - width) / 2).toInt(),
                ((bitmap.width * bigScale - width) / 2).toInt(),
                (-(bitmap.height * bigScale - height) / 2).toInt(),
                ((bitmap.height * bigScale - height) / 2).toInt(),
                // 弹性滑动，可选
                50.dp, 50.dp
            )
            /**
             * 在下一帧调用 runnable 里的内容
             */
//            postOnAnimation {
//                refreshFling()
//            }
            // 自己实现 runnable ，避免 lambda 频繁创建对象


            /**
             * 这两个方法都会在主线程执行传入的 runnable
             * post 会立即推到主线程
             * postOnAnimation 会在下一帧执行
             */
//            postOnAnimation(this)
//            post(this)
            // 推荐用它
            ViewCompat.postOnAnimation(this, this)

        }

        return false
    }

    override fun run() {
        if (scroller.computeScrollOffset()) {
            invalidate()
            scrollOffsetX = scroller.currX.toFloat()
            scrollOffsetY = scroller.currY.toFloat()
            postOnAnimation(this)
        }
    }


    /**
     * 手指头移动的时候，action_move 发生的时候
     * @param downEvent 按下的时候的事件
     * @param currentEvent 最新的当前事件
     * @param distanceX 这次事件的点和上次事件的两个点的距离；注意它是旧位置减新位置
     * @param distanceY 这次事件的点和上次事件的两个点的距离；注意它是旧位置减新位置
     *
     * 返回值没有意义
     */
    override fun onScroll(
        downEvent: MotionEvent?,
        currentEvent: MotionEvent?,
        distanceX: Float,
        distanceY: Float
    ): Boolean {
        if (big) {// 大图的时候才可以滑动
            scrollOffsetX -= distanceX // 因为 distanceX 是旧值减新值，所以这里是减等
            // 边界修正
            val modifyX = (bitmap.width * bigScale - width) / 2
            scrollOffsetX = min(scrollOffsetX, modifyX)
            scrollOffsetX = max(scrollOffsetX, -modifyX)

            scrollOffsetY -= distanceY // 因为 distanceX 是旧值减新值，所以这里是减等
            val modifyY = (bitmap.height * bigScale - height) / 2
            scrollOffsetY = min(scrollOffsetY, modifyY)
            scrollOffsetY = max(scrollOffsetY, -modifyY)
            invalidate()
        }
        return false
    }

    /**
     * 连续按两下，两次按下之间有一个很短的时间间隔（default 300ms）
     * 短于40ms 也不会触发，防手抖
     * 按下 - 抬起 - 再按下
     * 返回值没有意义
     */
    override fun onDoubleTap(e: MotionEvent): Boolean {
        big = !big
        if (big) {
            scrollOffsetX = (e.x - width / 2f) * (1 - bigScale / smallScale)
            scrollOffsetY = (e.y - height / 2f) * (1 - bigScale / smallScale)
            val modifyX = (bitmap.width * bigScale - width) / 2
            scrollOffsetX = min(scrollOffsetX, modifyX)
            scrollOffsetX = max(scrollOffsetX, -modifyX)
            val modifyY = (bitmap.height * bigScale - height) / 2
            scrollOffsetY = min(scrollOffsetY, modifyY)
            scrollOffsetY = max(scrollOffsetY, -modifyY)
            scaleAnimator.start()
        } else {
            scaleAnimator.reverse()
        }
        return true
    }

    /**
     * 双击之后的后续事件，action_move
     */
    override fun onDoubleTapEvent(e: MotionEvent?): Boolean {
        return false
    }

    /**
     * 和双击不会冲突的单击
     * 当你需要支持双击监听的时候，
     * 用这个方法来监测是否单击要比 onSingleTapUp(e: MotionEvent) 要准确
     */
    override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
        return false
    }

    inner class ZScaleGestureListener : ScaleGestureDetector.OnScaleGestureListener {

        /**
         * 捏撑开始
         * 需要返回 true
         */
        override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
            return true
        }

        /**
         *捏撑结束
         */
        override fun onScaleEnd(detector: ScaleGestureDetector) {
        }

        /**
         *捏撑过程中
         *
         */
        override fun onScale(detector: ScaleGestureDetector): Boolean {
            /**
             * 实时的放缩系数
             * detector.scaleFactor 的返回值
             * @return false 表示的此刻与初始状态的比值
             * @return true  表示的此刻与上一时刻的比值
             */
            detector.scaleFactor
            return true
        }

    }

}